package com.db.jschool.logging;

import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public abstract class Appender {
	
	private final Dispatcher dispathcer;
	private final String name;
	private final BlockingQueue<LogRecord> queue = new LinkedBlockingQueue<>(100000);
	private Layout layout;
	
	public Appender(String name) {
		this.name = name;
		dispathcer = new Dispatcher(queue);
		dispathcer.setDaemon(true);
		dispathcer.setName("Dispathcer-" + name);
		dispathcer.start();
	}
				
	public void publish(LogRecord lr) throws InterruptedException {
		queue.put(lr);
	}
	
	public synchronized void setLayout(Layout layout) {
		this.layout = layout;
	}

	protected abstract void append(String message) throws IOException;
	protected abstract void close();
	
	private class Dispatcher extends Thread {
		
		private final BlockingQueue<LogRecord> queue;
		private volatile boolean isActive;
		
		public Dispatcher(BlockingQueue<LogRecord> queue) {
			super(name);
			this.queue = queue;
		}
		
		@Override
		public void run() {
			while (true) {
				try {
					LogRecord lr;
					if (isActive) {
						lr = queue.take(); 
					} else {
						// Process pending log requests if there's any
						lr = queue.poll();
						if (lr == null) {
							break;
						}
					}
					
					append(layout.format(lr));
					
				} catch (InterruptedException e) {
					// Intentionally left empty
				} catch (IOException e) {
					System.err.println("Unable to append a message : " + e);
				} catch (Exception e) {
					System.err.println("Exception has occured: " + e);
				}
			}
			
			close();
		}
		
		@Override
		public synchronized void start() {
			isActive = true;
			super.start();
		}
		
		@Override
		public void interrupt() {
			isActive = false;
			super.interrupt();
		}
		
	}
}
